﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using OutLook.Properties.Calc.Structures;
using System.Collections;
using OutLook.Model.Infrastructure;

namespace OutLook.Properties.Calc
{
    public class SlagSystem
    {
        public SlagSystem(List<Coupla> slagsystem)
        {
            oxidecollection = new List<SlagSystemElement>();

            foreach (Coupla item in slagsystem)
            {
                if (item.Indicator == Constants.ELEMENT_S)
                {
                    item.Indicator = Constants.ELEMENT_Ca + Constants.ELEMENT_S;
                }
                SlagSystemElement slagSystemElement = new SlagSystemElement();
                slagSystemElement.Oxide = ElementsData.GetOxide(item.Indicator);
                slagSystemElement.Value = item.Value;
                slagSystemElement.Oxide.SetCationAndAnionWeight(slagSystemElement.Value);

                oxidecollection.Add(slagSystemElement);
            }
        }

        public void CalcSlagSystemProperties()
        {
            double anionSumm = 0;
            double kationSumm = 0;
            foreach (SlagSystemElement item in oxidecollection)
            {
                anionSumm = anionSumm + item.Oxide.AnionWeight;
                kationSumm = kationSumm + item.Oxide.CationWeight;
            }

            slagProperties = new List<Coupla>();

            Coupla Ro = Coupla.Create("Ro", kationSumm / anionSumm);

            slagProperties.Add(Ro);

            foreach (SlagSystemElement item in oxidecollection)
            {
                item.Oxide.SetCationAndAnionAverageWeight(kationSumm, anionSumm);
                tg_cation = tg_cation + item.Oxide.CationFeature.TgAlfa * item.Oxide.CationAverageWeight;
                tg_anion = tg_anion + item.Oxide.AnionFeature.TgAlfa * item.Oxide.AnionAverageWeight;
            }

            //Coupla Tg = Coupla.Create("tg", tg_cation);

            //slagProperties.Add(Tg);

            for (int i = 0; i < oxidecollection.Count - 1; i++)
            {
                for (int j = i + 1; j < oxidecollection.Count; j++)
                {
                    RemoveRepeat(oxidecollection[i], oxidecollection[j]);
                }
            }


            cationcollection = new List<Cation>();
            anioncollection = new List<Anion>();
            foreach (SlagSystemElement item in oxidecollection)
            {
                if (item.Oxide.CationName != "")
                {
                    Cation cation = Cation.Create(item.Oxide.CationName, item.Oxide.CationFeature.Mass, item.Oxide.CationFeature.Ru0, item.Oxide.CationFeature.TgAlfa, item.Oxide.CationFeature.Zy, item.Oxide.CationFeature.D, item.Oxide.CationAverageWeight);
                    cationcollection.Add(cation);
                }
                if (item.Oxide.AnionName != "")
                {
                    Anion anion = Anion.Create(item.Oxide.AnionName, item.Oxide.AnionFeature.Mass, item.Oxide.AnionFeature.Ru0, item.Oxide.AnionFeature.TgAlfa, item.Oxide.AnionFeature.Zy, item.Oxide.AnionFeature.D, item.Oxide.AnionAverageWeight);
                    anioncollection.Add(anion);
                }
            }

            double a = new double();
            double de = new double();
            Solve(ref a, ref de);

            Coupla De = Coupla.Create("De", de);

            slagProperties.Add(De);

        }

        private void Solve(ref double a, ref double de)
        {
            double a1 = new double();
            double a2 = new double();
            RootByDivision(ref a1, ref a2);
            
            double dzm = new double();
            
            Koren_a(a1, a2, ref a, ref de, ref dzm);
        }

        private void Koren_a(double a1, double a2, ref double a, ref double de, ref double dzm)
        {
            double eps1 = 0.000001;
            double eps2 = 0.000001;
            double[,] desh_k_a = new double[cationcollection.Count, anioncollection.Count];
            double[,] desh_a_a = new double[anioncollection.Count, anioncollection.Count];
            double[,] desh_k_k = new double[cationcollection.Count, cationcollection.Count];
            deltae(a1, ref desh_k_a, ref desh_a_a, ref desh_k_k);
            double F = new double();
            double[] Zar_kationa = new double[cationcollection.Count];
            double[] Rl_kationa = new double[cationcollection.Count];
            double[] Zar_aniona = new double[anioncollection.Count];
            double[] Rl_aniona = new double[anioncollection.Count];
            de = new double();
            Funk(a1, desh_k_a, desh_a_a, desh_k_k, ref F, ref de, ref dzm, ref Zar_kationa, ref Zar_aniona, ref Rl_kationa, ref Rl_aniona);
            double F1 = F;
            deltae(a2, ref desh_k_a, ref desh_a_a, ref desh_k_k);
            de = new double();
            Funk(a2, desh_k_a, desh_a_a, desh_k_k, ref F, ref de, ref dzm, ref Zar_kationa, ref Zar_aniona, ref Rl_kationa, ref Rl_aniona);
            double F2 = F;
            int sch = 1;
            double a3 = new double();
            double F3 = new double();
            if (F1 > 0)
            {
                do
                {
                    a3 = a2 - ((F2 * (a1 - a2)) / (F1 - F2));
                    deltae(a3, ref desh_k_a, ref desh_a_a, ref desh_k_k);
                    de = new double();
                    Funk(a3, desh_k_a, desh_a_a, desh_k_k, ref F, ref de, ref dzm, ref Zar_kationa, ref Zar_aniona, ref Rl_kationa, ref Rl_aniona);
                    F3 = F;
                    if ((Math.Abs(a3 - a2) < eps1) & (Math.Abs(F3) < eps2))
                    {
                        sch = 1;
                    }
                    else
                    {
                        sch = 0;
                        a2 = a3;
                        F2 = F3;
                    }

                } while (sch == 0);
            }
            else
            {
                do
                {
                    a3 = a1 - ((F1 * (a2 - a1)) / (F2 - F1));
                    deltae(a3, ref desh_k_a, ref desh_a_a, ref desh_k_k);
                    de = new double();
                    Funk(a3, desh_k_a, desh_a_a, desh_k_k, ref F, ref de, ref dzm, ref Zar_kationa, ref Zar_aniona, ref Rl_kationa, ref Rl_aniona);
                    F3 = F;
                    if ((Math.Abs(a3 - a1) < eps1) & (Math.Abs(F3) < eps2))
                    {
                        sch = 1;
                    }
                    else
                    {
                        sch = 0;
                        a1 = a3;
                        F1 = F3;
                    }
                } while (sch == 0);
            }
            a = a3;
        }

        private void RootByDivision(ref double a1, ref double a2)
        {
            a1 = 3;
            double[,] desh_k_a = new double[cationcollection.Count, anioncollection.Count];
            double[,] desh_a_a = new double[anioncollection.Count, anioncollection.Count];
            double[,] desh_k_k = new double[cationcollection.Count, cationcollection.Count];
            deltae(a1, ref desh_k_a, ref desh_a_a, ref desh_k_k);
            double F = new double();
            double dzm = new double();
            double[] Zar_kationa = new double[cationcollection.Count];
            double[] Rl_kationa = new double[cationcollection.Count];
            double[] Zar_aniona = new double[anioncollection.Count];
            double[] Rl_aniona = new double[anioncollection.Count];
            double de = new double();
            Funk(a1, desh_k_a, desh_a_a, desh_k_k, ref F, ref de, ref dzm, ref Zar_kationa, ref Zar_aniona, ref Rl_kationa, ref Rl_aniona);
            double F1 = F;
            double h = 0.05;
            double F2 = new double();
            a2 = new double();
            do
            {
                a2 = a1 + h;
                deltae(a2, ref desh_k_a, ref desh_a_a, ref desh_k_k);
                Funk(a2, desh_k_a, desh_a_a, desh_k_k, ref F, ref de, ref dzm, ref Zar_kationa, ref Zar_aniona, ref Rl_kationa, ref Rl_aniona);
                F2 = F;
                if (F1 * F2 > 0)
                {
                    if (Math.Abs(F2) > Math.Abs(F1))
                    {
                        h = -h;
                    }
                    else
                    {
                        a1 = a2;
                        F1 = F2;
                        h = 2 * h;
                    }
                }

            } while (F1 * F2 > 0);
        }

        private void Funk(double a, double[,] desh_k_a, double[,] desh_a_a, double[,] desh_k_k, ref double F, ref double De, ref double dzm, ref double[] Zar_kationa, ref double[] Zar_aniona, ref double[] Rl_kationa, ref double[] Rl_aniona)
        {
            double Zmin;
            double Z_a_ka = new double();
            double Z_k_ka = new double();
            double Z_k_kk = new double();
            double Ru_k = new double();
            double Ru_a = new double();
            double Otn_k = new double();
            double Otn_a = new double();
            double w1 = new double();
            double w2 = new double();
            double Z_a_aa = new double();
            for (int i = 0; i < cationcollection.Count; i++)
            {
                for (int j = 0; j < anioncollection.Count; j++)
                {
                    Zmin = Math.Log10((cationcollection[i].Ru0 * cationcollection[i].TgAlfa) / (anioncollection[j].Ru0 * anioncollection[j].TgAlfa)) / (cationcollection[i].TgAlfa + anioncollection[j].TgAlfa);
                    Z_a_ka = Z_a_ka + (-Zmin + desh_k_a[i, j] / 2) * cationcollection[i].Weight * anioncollection[j].Weight;
                    Z_k_ka = Z_k_ka + (Zmin + desh_k_a[i, j] / 2) * cationcollection[i].Weight * anioncollection[j].Weight;
                    Ru_k = Math.Pow(10, Math.Log10(cationcollection[i].Ru0) - (Zmin + desh_k_a[i, j] / 2) * cationcollection[i].TgAlfa);
                    Ru_a = Math.Pow(10, Math.Log10(anioncollection[j].Ru0) - (-Zmin + desh_k_a[i, j] / 2) * anioncollection[j].TgAlfa);
                    Otn_k = Otn_k + (Ru_k / Ru_a) * cationcollection[i].Weight * anioncollection[j].Weight;
                    Otn_a = Otn_a + (Ru_a / Ru_k) * cationcollection[i].Weight * anioncollection[j].Weight;
                    De = De + desh_k_a[i, j] * cationcollection[i].Weight * anioncollection[j].Weight;
                    Zar_aniona[j] = Zar_aniona[j] + (Z_a_ka - w1) / anioncollection[j].Weight;
                    w1 = Z_a_ka;
                    Rl_kationa[i] = Rl_kationa[i] + (0.434 / (Ru_k * cationcollection[i].TgAlfa)) * cationcollection[i].Weight * anioncollection[j].Weight;
                    Rl_aniona[j] = Rl_aniona[j] + (0.434 / (Ru_a * anioncollection[j].TgAlfa)) * cationcollection[i].Weight * anioncollection[j].Weight;
                }
                Zar_kationa[i] = (Z_k_ka - w2) / cationcollection[i].Weight;
                w2 = Z_k_ka;
            }

            if (anioncollection.Count == 1)
            {
                Z_a_aa = -Math.Log10(a * 0.707 / anioncollection[1].Ru0) / anioncollection[1].TgAlfa;
            }
            else
            {
                for (int i = 0; i < anioncollection.Count; i++)
                {
                    for (int j = 0; j < anioncollection.Count; j++)
                    {
                        Zmin = Math.Log10((anioncollection[i].Ru0 * anioncollection[i].TgAlfa) / (anioncollection[j].Ru0 * anioncollection[j].TgAlfa)) / (anioncollection[i].TgAlfa + anioncollection[j].TgAlfa);
                        Z_a_aa = Z_a_aa + (Zmin + desh_a_a[i, j] / 2) * anioncollection[i].Weight * anioncollection[j].Weight;
                    }
                }
            }

            for (int i = 0; i < cationcollection.Count; i++)
            {
                for (int j = 0; j < cationcollection.Count; j++)
                {
                    Zmin = Math.Log10((cationcollection[i].Ru0 * cationcollection[i].TgAlfa) / (cationcollection[j].Ru0 * cationcollection[j].TgAlfa)) / (cationcollection[i].TgAlfa + cationcollection[j].TgAlfa);
                    Z_k_kk = Z_k_kk + (Zmin + desh_k_k[i, j] / 2) * cationcollection[i].Weight * cationcollection[j].Weight;
                }
            }

            F = Z_a_ka - Z_a_aa - ((Otn_k - 0.48) / (6.067 * tg_anion - 0.1927)) - 0.275;
            double qw = Math.Pow(tg_cation, 1.5075);
            dzm = Z_k_ka - Z_k_kk - ((Otn_a - 0.53) / (15.45 * qw)) - 0.51;
        }

        private void deltae(double a, ref double[,] desh_k_a, ref double[,] desh_a_a, ref double[,] desh_k_k)
        {
            double[,] Zmin = new double[cationcollection.Count + anioncollection.Count, cationcollection.Count + anioncollection.Count];
            double[,] c0 = new double[cationcollection.Count + anioncollection.Count, cationcollection.Count + anioncollection.Count];
            double[,] c1 = new double[cationcollection.Count + anioncollection.Count, cationcollection.Count + anioncollection.Count];
            double[,] c2 = new double[cationcollection.Count + anioncollection.Count, cationcollection.Count + anioncollection.Count];
            double[,] c3 = new double[cationcollection.Count + anioncollection.Count, cationcollection.Count + anioncollection.Count];
            double[,] c4 = new double[cationcollection.Count + anioncollection.Count, cationcollection.Count + anioncollection.Count];

            for (int i = 0; i < cationcollection.Count; i++)
            {
                for (int j = 0; j < anioncollection.Count; j++)
                {
                    Zmin[i, j] = Math.Log10((cationcollection[i].Ru0 * cationcollection[i].TgAlfa) / (anioncollection[j].Ru0 * anioncollection[j].TgAlfa)) / (cationcollection[i].TgAlfa + anioncollection[j].TgAlfa);
                    c0[i, j] = a;
                    c1[i, j] = Math.Log10(cationcollection[i].Ru0) - (cationcollection[i].TgAlfa * Zmin[i, j]);
                    c2[i, j] = cationcollection[i].TgAlfa / 2;
                    c3[i, j] = Math.Log10(anioncollection[j].Ru0) + (anioncollection[j].TgAlfa * Zmin[i, j]);
                    c4[i, j] = anioncollection[j].TgAlfa / 2;
                    double dee1 = new double();
                    double dee2 = new double();
                    OtdKorde(c0[i, j], c1[i, j], c2[i, j], c3[i, j], c4[i, j], ref dee1, ref dee2);
                    double ddee = new double();
                    Korende(c0[i, j], c1[i, j], c2[i, j], c3[i, j], c4[i, j], dee1, dee2, ref ddee);
                    desh_k_a[i, j] = ddee;
                }
            }

            for (int i = 0; i < anioncollection.Count; i++)
            {
                for (int j = 0; j < anioncollection.Count; j++)
                {
                    Zmin[i, j] = Math.Log10((anioncollection[i].Ru0 * anioncollection[i].TgAlfa) / (anioncollection[j].Ru0 * anioncollection[j].TgAlfa)) / (anioncollection[i].TgAlfa + anioncollection[j].TgAlfa);
                    c0[i, j] = a * 1.414;
                    c1[i, j] = Math.Log10(anioncollection[i].Ru0) - (anioncollection[i].TgAlfa * Zmin[i, j]);
                    c2[i, j] = anioncollection[i].TgAlfa / 2;
                    c3[i, j] = Math.Log10(anioncollection[j].Ru0) + (anioncollection[j].TgAlfa * Zmin[i, j]);
                    c4[i, j] = anioncollection[j].TgAlfa / 2;
                    double dee1 = new double();
                    double dee2 = new double();
                    OtdKorde(c0[i, j], c1[i, j], c2[i, j], c3[i, j], c4[i, j], ref dee1, ref dee2);
                    double ddee = new double();
                    Korende(c0[i, j], c1[i, j], c2[i, j], c3[i, j], c4[i, j], dee1, dee2, ref ddee);
                    desh_a_a[i, j] = ddee;
                }
            }

            for (int i = 0; i < cationcollection.Count; i++)
            {
                for (int j = 0; j < cationcollection.Count; j++)
                {
                    Zmin[i, j] = Math.Log10((cationcollection[i].Ru0 * cationcollection[i].TgAlfa) / (cationcollection[j].Ru0 * cationcollection[j].TgAlfa)) / (cationcollection[i].TgAlfa + cationcollection[j].TgAlfa);
                    c0[i, j] = a * 1.414;
                    c1[i, j] = Math.Log10(cationcollection[i].Ru0) - (cationcollection[i].TgAlfa * Zmin[i, j]);
                    c2[i, j] = cationcollection[i].TgAlfa / 2;
                    c3[i, j] = Math.Log10(cationcollection[j].Ru0) + (cationcollection[j].TgAlfa * Zmin[i, j]);
                    c4[i, j] = cationcollection[j].TgAlfa / 2;
                    double dee1 = new double();
                    double dee2 = new double();
                    OtdKorde(c0[i, j], c1[i, j], c2[i, j], c3[i, j], c4[i, j], ref dee1, ref dee2);
                    double ddee = new double();
                    Korende(c0[i, j], c1[i, j], c2[i, j], c3[i, j], c4[i, j], dee1, dee2, ref ddee);
                    desh_k_k[i, j] = ddee;
                }
            }
        }

        private void Korende(double c00, double c11, double c22, double c33, double c44, double dee1, double dee2, ref double ddee)
        {
            double eps1 = 0.000001;
            double eps2 = 0.000001;
            double F1 = Math.Pow(10, (c11 - c22 * dee1)) + Math.Pow(10, (c33 - c44 * dee1)) - c00;
            double F2 = Math.Pow(10, (c11 - c22 * dee2)) + Math.Pow(10, (c33 - c44 * dee2)) - c00;
            double dee3;
            int sch = 1;
            if (F1 > 0)
            {
                do
                {
                    dee3 = dee2 - ((F2 * (dee1 - dee2)) / (F1 - F2));
                    double F3 = Math.Pow(10, (c11 - c22 * dee3)) + Math.Pow(10, (c33 - c44 * dee3)) - c00;
                    if ((Math.Abs(dee3 - dee2) < eps1) & (Math.Abs(F3) < eps2))
                    {
                        sch = 1;
                    }
                    else
                    {
                        sch = 0;
                        dee2 = dee3;
                        F2 = F3;
                    }

                } while (sch == 0);
            }
            else
            {
                do
                {
                    dee3 = dee1 - ((F1 * (dee2 - dee1)) / (F2 - F1));
                    double F3 = Math.Pow(10, (c11 - c22 * dee3)) + Math.Pow(10, (c33 - c44 * dee3)) - c00;
                    if ((Math.Abs(dee3 - dee1) < eps1) & (Math.Abs(F3) < eps2))
                    {
                        sch = 1;
                    }
                    else
                    {
                        sch = 0;
                        dee1 = dee3;
                        F1 = F3;
                    }

                } while (sch == 0);
            }
            ddee = dee3;
        }

        private void OtdKorde(double c00, double c11, double c22, double c33, double c44, ref double dee1, ref double dee2)
        {
            dee1 = 1;
            double Fsh1 = Math.Pow(10, (c11 - c22 * dee1)) + Math.Pow(10, (c33 - c44 * dee1)) - c00;
            double h = 0.1;
            double Fsh2;
            do
            {
                dee2 = dee1 + h;
                Fsh2 = Math.Pow(10, (c11 - c22 * dee2)) + Math.Pow(10, (c33 - c44 * dee2)) - c00;
                if (Fsh1 * Fsh2 > 0)
                {
                    if (Math.Abs(Fsh2) > Math.Abs(Fsh1))
                    {
                        h = -h;
                    }
                    else
                    {
                        dee1 = dee2;
                        Fsh1 = Fsh2;
                        h = 2 * h;
                    }
                }

            } while (Fsh1 * Fsh2 > 0);
        }

        private void RemoveRepeat(SlagSystemElement item1, SlagSystemElement item2)
        {
            if (item1.Oxide.CationName == item2.Oxide.CationName)
            {
                item1.Oxide.CationAverageWeight = item1.Oxide.CationAverageWeight + item2.Oxide.CationAverageWeight;
                item2.Oxide.CationName = "";
            }

            if (item1.Oxide.AnionName == item2.Oxide.AnionName)
            {
                item1.Oxide.AnionAverageWeight = item1.Oxide.AnionAverageWeight + item2.Oxide.AnionAverageWeight;
                item2.Oxide.AnionName = "";
            }

        }

        #region Properties

        /// <summary>
        /// Properties of slag system (any which come: load, slag, first slag)
        /// </summary>
        public List<Coupla> slagProperties { get; set; }

        #endregion

        #region Private Properties

        /// <summary>
        /// 
        /// </summary>
        private double tg_cation { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private double tg_anion { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private List<SlagSystemElement> oxidecollection { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private List<Cation> cationcollection { get; set; }

        /// <summary>
        /// 
        /// </summary>
        private List<Anion> anioncollection { get; set; }

        #endregion

    }

}
